package cn.idu.socket.sample0410;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class NioHttpClient {

    private static Selector selector;
    private Charset charset = StandardCharsets.UTF_8;

    static {
        try {
            selector = Selector.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    SocketChannel socketChannel;

    public static void main(String[] args) throws IOException {
        NioHttpClient client = new NioHttpClient();
        for (int i = 0; i < 10; i++) {
            client.request("www.baidu.com", 80);
        }
        client.select();
    }


    public void request(String host, int port) throws IOException {
        InetSocketAddress remote = new InetSocketAddress(host, port);
        socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.socket().setSoTimeout(5000);
        socketChannel.connect(remote);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE, buffer);
    }

    private void select() throws IOException {
        while (selector.select(500) > 0) {
            Set keys = selector.selectedKeys();
            Iterator iterator = keys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = (SelectionKey) iterator.next();
                iterator.remove();
                if (key.isConnectable()) connect(key);
                if (key.isWritable()) write(key);
                if (key.isReadable()) receive(key);
            }
        }
    }

    private void receive(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.clear();

        int read = channel.read(buffer);
        System.out.println("read: "+read);
        System.out.println(String.format("before flip position:%d , limit:%d", buffer.position(), buffer.limit()));
        buffer.flip();
        System.out.println(String.format("after flip position:%d , limit:%d", buffer.position(), buffer.limit()));
        System.out.println("flip 后 hasRemaining：" + buffer.hasRemaining());
        String receiveData = charset.decode(buffer).toString();

        if ("".equals(receiveData)) {
            key.cancel();
            channel.close();
            return;
        }
        System.out.println(receiveData);
    }

    private void write(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        InetSocketAddress address = (InetSocketAddress) channel.socket().getRemoteSocketAddress();
        String host = address.getHostName();
        int port = address.getPort();
        String request = HttpUtil.conpositeRequest(host);
        System.out.println("写入请求：" + channel.getRemoteAddress() + " : " + request);

        channel.write(charset.encode(request));
        key.interestOps(SelectionKey.OP_READ);
    }

    private void connect(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        channel.finishConnect();
        InetSocketAddress address = (InetSocketAddress) channel.socket().getRemoteSocketAddress();
        String host = address.getHostName();
        int port = address.getPort();
        System.out.println(String.format("访问地址：%s:%d！", host, port));
    }


    private BufferedReader getReader(Socket socket) throws IOException {
        InputStream inputStream = socket.getInputStream();
        return new BufferedReader(new InputStreamReader(inputStream));
    }

    private PrintWriter getWriter(Socket socket) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        return new PrintWriter(new OutputStreamWriter(outputStream));
    }


}
